/*
 * WebSocketServer.ino
 *
 *  Created on: 22.05.2015
 *
 */

#include <Arduino.h>

#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClientSecure.h>
#include <WebSocketsServer.h>
#include <WebSocketsClient.h>
#include <cJSON.h>

WebSocketsServer webSocketServer = WebSocketsServer(9501);
WebSocketsClient webSocketClient;
char sn[10] = "g1";
//定义一个客户端结构体
typedef struct client_arr
{
  int fd;
  char *sn;
} CLIENTS;
//连接数组
CLIENTS clients[16];
char link_num = 0;

#define USE_SERIAL Serial

void hexdump(const void *mem, uint32_t len, uint8_t cols = 16)
{
  const uint8_t *src = (const uint8_t *)mem;
  USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
  for (uint32_t i = 0; i < len; i++)
  {
    if (i % cols == 0)
    {
      USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
    }
    USE_SERIAL.printf("%02X ", *src);
    src++;
  }
  USE_SERIAL.printf("\n");
}

void addClient(int num, char *sn)
{
  if (link_num >= 16)
  {
    Serial.println("当前网关设备已达最大数量");
    return;
  }

  int p = 0;
  char is_update = 0;
  for (int i = 0; i < 16; i++)
  {
    if (num == clients[i].fd)
    {
      is_update = 1;
      p = i;
      break;
    }
    if (strcmp(clients[i].sn, sn) == 0)
    {
      is_update = 1;
      p = i;
      break;
    }
    if (!strlen(clients[i].sn))
    {
      is_update = 0;
      p = i;
      break;
    }
  }
  clients[p] = {num, sn};
  if (!is_update)
  {
    link_num++;
  }
  Serial.println("clients:");
  for (int i = 0; i < 16; i++)
  {
    if (strlen(clients[i].sn))
      Serial.printf("%d:%s\n", clients[i].fd, clients[i].sn);
  }
  //转发给云端注册设备
  char msg[64] = "";
  webSocketClient.sendTXT(sprintf(msg, "{\"type\":1,\"sn\":\"%s\"}", sn));
}

int getClientfd(char *sn)
{
  int clientnum = -1;
  for (int i = 0; i < 16; i++)
  {
    if (strcmp(clients[i].sn, sn) == 0)
    {
      clientnum = clients[i].fd;
    }
  }
  return clientnum;
}

void webSocketServerEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
{

  switch (type)
  {
  case WStype_DISCONNECTED:
    USE_SERIAL.printf("[%u] Disconnected!\n", num);
    break;
  case WStype_CONNECTED:
  {
    IPAddress ip = webSocketServer.remoteIP(num);
    USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);

    // send message to client
    webSocketServer.sendTXT(num, "Connected");
  }
  break;
  case WStype_TEXT:
  {
    USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
    //解析json
    char *data = (char *)payload;
    cJSON *pJsonRoot = cJSON_Parse(data);
    if (!pJsonRoot)
    {
      Serial.println("json err");
      break;
    }
    //消息类型
    int msgType = cJSON_GetObjectItem(pJsonRoot, "type")->valueint;
    char *sn = cJSON_GetObjectItem(pJsonRoot, "sn")->valuestring;
    if (!msgType)
    {
      Serial.println("no type msg");
      break;
    }
    //按type做业务处理
    switch (msgType)
    {
    case 1: //注册
    {
      addClient(num,sn);
    }
    break;
    case 2: //接收上传数据
    {
      if(getClientfd(sn) == -1){
        addClient(num,sn);
      }
      int data = cJSON_GetObjectItem(pJsonRoot, "data")->valueint;
      int status = 0;
      //判断并给出指令
      if (data > 600)
      {
        status = 1;
      }
      char msg[128] = "";
      sprintf(msg, "{\"status\":%d,\"type\":2}", status);
      webSocketServer.sendTXT(num, msg);
    }
    break;
    default:
      break;
    }
    //转发给云端服务器
    webSocketClient.sendTXT(payload);
  }
  break;
  case WStype_BIN:
    USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
    hexdump(payload, length);

    // send message to client
    // webSocket.sendBIN(num, payload, length);
    break;
  case WStype_ERROR:
  case WStype_FRAGMENT_TEXT_START:
  case WStype_FRAGMENT_BIN_START:
  case WStype_FRAGMENT:
  case WStype_FRAGMENT_FIN:
  case WStype_PING:
  case WStype_PONG:
    break;
  }
}

void webSocketClientEvent(WStype_t type, uint8_t *payload, size_t length)
{
  switch (type)
  {
  case WStype_DISCONNECTED:
    USE_SERIAL.printf("[WSc] Disconnected!\n");
    break;
  case WStype_CONNECTED:
  {
    USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
    //注册连接
    char msg[64] = "";
    sprintf(msg, "{\"type\":1,\"sn\":\"%s\"}", sn);
    webSocketClient.sendTXT(msg);
  }
  break;
  case WStype_TEXT:
  {
    USE_SERIAL.printf("[WSc] get text: %s\n", payload);
    //解析json
    char *data = (char *)payload;
    cJSON *pJsonRoot = cJSON_Parse(data);
    if (!pJsonRoot)
    {
      Serial.println("json err");
      break;
    }
    //消息类型
    int msgType = cJSON_GetObjectItem(pJsonRoot, "type")->valueint;
    if(!msgType){
      Serial.println("no need deal");
      break;
    }
    char *targetSn = cJSON_GetObjectItem(pJsonRoot, "sn")->valuestring;
    switch (msgType)
    {
    case 3: //接收云端指令转发给终端控制指令
    {
      int status = cJSON_GetObjectItem(pJsonRoot, "status")->valueint;
      //按sn找到连接句柄
      int clientnum = getClientfd(targetSn);
      if (clientnum == -1)
      {
        Serial.println("没有找到此终端设备");
        webSocketClient.sendTXT("没有找到此终端设备");
        break;
      }
      char msg[128] = "";
      sprintf(msg, "{\"status\":%d,\"type\":3}", status);
      //转发给子设备
      webSocketServer.sendTXT(clientnum, msg);
    }
    break;
    case 4: //接收云端指令转发给终端设置指令
    {
      int freq = cJSON_GetObjectItem(pJsonRoot, "freq")->valueint;
      int hand_delay = cJSON_GetObjectItem(pJsonRoot, "hand_delay")->valueint;
      //按sn找到连接句柄
      int clientnum = getClientfd(targetSn);
      if (clientnum == -1)
      {
        Serial.println("没有找到此终端设备");
        webSocketClient.sendTXT("没有找到此终端设备");
        break;
      }
      char msg[128] = "";
      sprintf(msg, "{\"freq\":%d,\"hand_delay\":%d,\"type\":4}", freq, hand_delay);
      //转发给子设备
      webSocketServer.sendTXT(clientnum, msg);
    }
    break;
    default:
      break;
    }
  }
    
  break;
  case WStype_BIN:
    USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
    hexdump(payload, length);

    // send data to server
    // webSocket.sendBIN(payload, length);
    break;
  case WStype_PING:
    // pong will be send automatically
    USE_SERIAL.printf("[WSc] get ping\n");
    break;
  case WStype_PONG:
    // answer to a ping we send
    //USE_SERIAL.printf("[WSc] get pong\n");
    break;
  default:
    break;
  }
}

void wsClientTask(void *pvParameters)
{
  while (1)
  {
    webSocketClient.loop();
  }
}

void setup()
{
  USE_SERIAL.begin(9600);
  USE_SERIAL.setDebugOutput(true);

  for (uint8_t t = 1; t > 0; t--)
  {
    USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
    USE_SERIAL.flush();
    delay(1000);
  }

  //初始化连接数组
  for (int i = 0; i < 16; i++)
  {
    clients[i] = {0, (char*)""};
  }
  link_num = 0;

  WiFi.begin("AcTek_Wifi", "actek888");

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(100);
    Serial.print(".");
  }
  Serial.println(WiFi.localIP());

  webSocketServer.begin();
  webSocketServer.onEvent(webSocketServerEvent);

  webSocketClient.begin("192.168.88.198", 9501, "/");
  webSocketClient.onEvent(webSocketClientEvent);
  webSocketClient.setReconnectInterval(5000);
  webSocketClient.enableHeartbeat(15000, 3000, 2);

  // xTaskCreate(wsClientTask,"wsClientTask",20480,NULL,0,NULL);

}

void loop()
{
  webSocketServer.loop();
  webSocketClient.loop();
}
